#ifndef HEAD_H
#define HEAD_H

#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <mysql/mysql.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <pwd.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/sendfile.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

// 日志级别
typedef enum {
    DEBUG,
    INFO,
    WARN,
    ERROR
} log_level;

void write_log(log_level level, const char *msg, const char *filename, const char *funcname, int line, const char *username, const char *action);

/**
 * @brief 日志宏函数(日志文件的路径已经在函数中写死, 有需要可以提取参数)
 * @param level 日志级别: 有DEBUG, INFO, WARN, ERROR
 * @param username 用户名
 * @param action 写用户的行为信息
 * @param msg 其他输出信息(如错误信息等)
 */
#define LOG(level, username, action, msg)                                          \
    do {                                                                           \
        write_log(level, msg, __FILE__, __FUNCTION__, __LINE__, username, action); \
    } while (false)

// 用于检查命令行参数是否符合预期的宏函数
#define ARGS_CHECK(argc, expected)                \
    do {                                          \
        if ((argc) != (expected)) {               \
            fprintf(stderr, "args num error!\n"); \
            exit(EXIT_FAILURE);                   \
        }                                         \
    } while (false)

// 用于检查函数的返回值是否为错误标记，并进行相应的错误处理
#define ERROR_CHECK(ret, error_flag, msg) \
    do {                                  \
        if ((ret) == (error_flag)) {      \
            perror(msg);                  \
            exit(EXIT_FAILURE);           \
        }                                 \
    } while (false)

#define THREAD_ERROR_CHECK(ret, msg)                         \
    do {                                                     \
        if ((ret) != 0) {                                    \
            fprintf(stderr, "%s: %s\n", msg, strerror(ret)); \
        }                                                    \
    } while (false)

typedef enum {
    LOGIN,  // 登录
    CD,
    LS,
    PUTS,
    GETS,
    RM,
    PWD,
    MKDIR,
    EXIT,
} request_type;

// 请求报文
typedef struct
{
    request_type flag;  // 请求类型
    char user_name[60];
    char args[60];
    char path[100];  // 路径名
} request_t;

// 响应报文
typedef struct
{
    int status;
    char args[1024];  // 服务端返回的参数
    // int file_size;   // 已经上传文件大小
} response_t;

/**
 * @brief 加载配置文件中的配置信息
 * 1.配置文件和server文件夹在同一目录下
 * 2.配置文件名为config.ini, 键值对以" = "分隔(等号两边有空格)
 * @param key 获取配置文件中对应的key
 * @param value 获取配置文件中对应的value
 * @return int 成功返回0, 失败返回-1
 */
int load_config(char *key, char *value);

/**
 * @brief 分析从stdin读取的命令和参数
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int analyze_cmd(int net_fd);

int gotodo(request_t *request, int net_fd);

int login(request_t *request, int net_fd);

/**
 * @brief cd命令的处理逻辑
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_cd(request_t *request, int net_fd);

/**
 * @brief ls命令的处理逻辑
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_ls(request_t *request, int net_fd);

/**
 * @brief puts将本地文件上传至服务器
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_puts(request_t *request, int net_fd);

/**
 * @brief gets将服务器上的文件下载至本地
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_gets(request_t *request, int net_fd);

/**
 * @brief rm命令的处理逻辑
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_rm(request_t *request, int net_fd);

/**
 * @brief pwd命令的处理逻辑
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_pwd(request_t *request, int net_fd);

/**
 * @brief mkdir命令的处理逻辑
 * @param request 传入传出的结构体
 * @return int 成功返回0, 失败返回-1
 */
int net_mkdir(request_t *request, int net_fd);

/**
 * @brief 用于给客户端发送文件
 * @param request 传入传出的结构体
 * @param net_fd 与客户端通信的socket
 * @return int 成功返回0, 失败返回-1
 */

// 向客户端发送文件
// full_path 文件的绝对路径
// net_fd 目标socket对象的描述符
// offset:发送的起始位置
int send_file(const char *full_path, int net_fd);

// 接收客户端上传的文件
// full_path 文件的绝对路径
// net_fd 目标socket对象的描述符
// file_size:文件大小
int recv_file(const char *full_path, int net_fd);

// 队列
typedef struct node_s {
    int net_fd;
    struct node_s *pNext;
} node_t;

typedef struct queue_s {
    node_t *head;
    node_t *end;
    int size;
} queue_t;

int enQueue(queue_t *pQueue, int net_fd);

int deQueue(queue_t *pQueue);

// 通信函数
typedef struct pool_s {
    // 所有子线程的id:数组
    pthread_t *thread_ids;
    // 子线程的数量
    int thread_num;
    // 任务队列
    queue_t queue;
    // 线程锁
    pthread_mutex_t lock;
    // 条件变量
    pthread_cond_t cond;

    // 退出标记位，0表示不退出，1表示退出
    int exitFlag;
} pool_t;

// 初始化数据库
int initMysql(MYSQL **mysql);
// 登录连接数据库
int loginConnMysql(MYSQL **mysql);
// 查文件连接数据库
int fileConnMysql(MYSQL **mysql);
// 查表看是否有用户，没有就创建，有就登录
// TODO？在这里？
// 文件操作，TODO？在这里？

// 根据指定数量创建线程池
int initPool(pool_t *pool, int num);
// 定义线程的入口函数
void *threadMain(void *p);

// 初始化连接
int initSocket(int *socket_fd);

// 添加epoll监听
int addEpoll(int epoll_fd, int filefd);

#endif
